package com.jd.limiter.aspect;

import com.google.common.collect.Lists;
import com.jd.limiter.annotation.AccessLimiter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.stream.Collectors;

@Slf4j
@Aspect
@Component
public class AccessLimiterAspect {

        @Autowired
        private StringRedisTemplate stringRedisTemplate;

        @Resource
        private RedisScript<Boolean> rateLimitLua;


        @Pointcut("@annotation(com.jd.limiter.annotation.AccessLimiter)")
        public void cut(){

        }

        @Before("cut()")
        public void before(JoinPoint joinPoint){
            //1、获取方法前面作为mehtodKey
            MethodSignature methodSignature=(MethodSignature) joinPoint.getSignature();
            Method method=methodSignature.getMethod();
            AccessLimiter annotation= method.getAnnotation(AccessLimiter.class);
            if(annotation==null){
                return;
            }
            String key=annotation.methodKey();
            Integer limit=annotation.limit();
            //如果没有设置methodKey，从调用方法前面生成自动一个key
            if(StringUtils.isEmpty(key)){
                Class[]type=method.getParameterTypes();
                key=method.getName();
                if(type!=null&&type.length>0){
                    String paramTypes= Arrays.stream(type).map(Class::getName)
                            .collect(Collectors.joining(","));
                    log.info("param types:"+paramTypes);
                    key+="#"+paramTypes;
                }
            }
            //2、调用redis
            boolean acquired=stringRedisTemplate.execute(rateLimitLua,  //lua script真身
                    Lists.newArrayList(key),   //lua 脚本中的key列表
                    limit.toString()     //lua脚本value列表
            );
            if(!acquired){
                log.error("your access is blocked,key={}",key);
                throw  new RuntimeException("your accesss is blocked");
            }
        }

}
